文章目录
  1. 1. VR 为何需要性能模式
  2. 2. 场景模式
  3. 3. 设置渲染线程优先级
  4. 4. cpu 绑定
    1. 4.1. 绑定渲染线程 cpu
    2. 4.2. 绑定显示中断 cpu

VR 为何需要性能模式

由于 VR 这个应用场景的特殊性,它需要极低的显示延迟,否则就会出现眩晕感(VR 这套原理网上有,当然也可看看我写的 headtracking 相关文章:VR HeadTracking 分析)。所以用 android 平台跑 VR 业务,是不能使用 android 原来那套显示框架的。那套 Vysnc + 三重缓冲 的框架,显示延迟至少一个 Vysnc 周期(原因可以看我这篇文章的相关分析:Activity 启动速度分析方法(启动流程分析)#Vsync简介 )。所以就需要 Single Buffer + Direct Render。这个就要求在一个 Vsync 周期里完成 应用渲染 + ATW 反畸变,反色差后处理送帧。VR 应用 和 普通 android 应用 的区别:

  1. VR 应用都是 OpenGL 3D 渲染的(我们用的引擎是 Unity引擎 或是 oculus ovr 那套),而且还是双目渲染,GPU loading 是比普通的 andrid 应用高不少。
  2. 普通 android 平台,应用渲染完 SurfaceFlinger 直接送显就行了,VR 还需要反畸变、反色差后处理(这个原因网上也有,简单的说是由于 VR 的光学镜片造成的畸变,需要图像处理进行矫正)。业界基本上是拿 GPU 做的,这让 GPU loading 进一步提高(我之前开发的平台这块是硬件处理的)。
  3. 我开发 VR 的时候是 2017年 那会,VR 主流分辨率是 2560x1440@70Hz,基本手机的刷新率是 60Hz。这个显示规格明显高了手机一截。不像现在(2019年)手机上有 90Hz、120Hz 了,但是即便手机上有 90Hz 和 120Hz 也不是一直都是这个刷新率(大部分时候还是 60Hz),而且 VR 是一直要保持 70Hz 这个刷新率的(VR 需要高刷新率是为了更低的延迟)。分辨率和刷新率的提升,也加重了系统的负载。
  4. 普通 android 场景偶尔有几次丢帧关系不大,但是在 VR 里面就会对用户造强烈的不舒适。VR 要求不能丢帧。

所以 VR 场景对系统的性能要求极高。而我开发 VR 的平台是一个 28nm 的 4核 1.8G 的 A53,GPU 也只有 Mali 760 mp2,而我们对标的竞品是 高通骁龙820,简直是青铜对线王者。要让应用达到稳定渲染稳定 60fps 不丢帧(应用渲染 60fps,ATW 插帧插到 70fps),需要极限压榨硬件性能。所以才有了 VR 性能模式这个说法,这个其实是下面几个措施的合称。因为涉及到的代码都是 vendor 的相关实现,所以这里不说具体代码实现,只是介绍一些措施思路而已。

场景模式

android 有一个 power hal,在里面可以定义一些 场景模式,不同的场景模式可以通过写硬件的文件节点控制频率,开关核等操作。对应到 PowerManager 接口就是 PowerHit:

1
2
3
4
5
6
/**
* The hintId sent through this method should be in-line with the
* PowerHint defined in vr/hardware/power/<version 1.0 & up>/IPower.h
*/
public abstract void powerHint(int hintId, int data);

hintId 是 场景编号,这个接口不对外公开,只在 SystemService 里调用。框架如下:

插图

可以在应用使能 VrMode (Vr Mode 是 Android 7.1 的时候加入的)的时候,通过这个接口切换到对应的 场景模式。例如我开发的 VR 平台为了保证性能,VR 的场景模式将 CPU 的最低频率锁定到一个比较高的值;关闭 cpu idle,强制 4核 全部开启;GPU 最低频率也锁定到一个比较高的值。那为什么不直接锁最高频率,因为 soc 的最高频率一般都只是一个理论值(低端 soc 基本适用),最高频率只要保持一段时间,基本上就要温控强制 降频+关核 了。

这里再多介绍一下,为什么手动锁最低频率,而不是依靠调频策略根据当前负载动态调频。一般调频策略都会有个延后性,一般来说 cpu(gpu)要提频,都要负载高于某个阀值保持一段时间才会提。这个就涉及到调频算法的优劣了,我开发 VR 平台那个算法,额, 就是简单粗错达到了某个点就升,低于那个点了就降。在 VR 场景一会负载高一点,一会又稍微低一点,频率忽高忽低很不稳定,导致应用帧率波动很大。 而我们要求的是稳定 60fps,所以这里就需要手动把最低频率锁定在某个合适的频率。还留有一定的调节空间,兼顾负载峰值和功耗。

设置渲染线程优先级

android 是 linux 内核的,是一个多任务系统。在 VR 应用前台渲染的时候,还有很多后台任务在跑。所以可以提升渲染线程的优先级(设置线程的 nice 值),让渲染线程更优先得到 cpu 资源。可以拉一个接口,让应用把自己的渲染线程的 tid 传过来,在系统里面进行配置。

cpu 绑定

我开发的 VR 平台是 4核 的,我发现大部分任务都默认被分配到了 0,1 核心(4个核心的编号是 cpu0,cpu1,cpu2、cpu3),包括了应用渲染线程。这就造成了渲染线程需要和其他任务强 cpu 资源。但是 2、3 核的任务就比较轻。于是我们就可以设计一下:让渲染线程跑到 2、3 核心。让其有足够的 cpu 资源,充分发挥4核 cpu 的优势(我们的 cpu 是 SMP 架构的,不是现在流行的大小核的,所以4个核的算力是一样的)。

绑定渲染线程 cpu

linux 有一个叫 cgroup 的概念。就是可以创建一个组别(cpu group),配置这个组别的 cpu 运行资源,其中就可以指定这个组别的任务跑到哪几个 cpu 上面。我们就可以创建一个 VR组别,配置这个组别的任务跑到 2,3 核上。再拉个接口,让 VR 应用把自己的 uid 传过来就行,把 VR 应用加入到 VR组 就能让 VR 应用渲染线程跑到 2、3 核了,不去和其他线程抢 cpu 资源。

在 开启VR场景模式 + 给渲染线程设置高优先级 + 绑定空闲 cpu 核心 后,VR应用渲染线程的 cpu运行时间 比以前有了很大的改善,帧率稳定了很多。systrace 能看出来(上面是没开启前,下面是开启后的,可以看到 systrace 基本都是绿的 Running 状态,基本没有等待状态了):

插图

插图

绑定显示中断 cpu

有了上面的措施后,在某些场景还是会卡断掉帧。分析过后发现是显示中断受了影响(Vysnc 就是显示中断产生的)。例如 mtp copy 创建,短时间内 mtp 中断频繁发生,抢占了显示中断的 cpu 资源(用户线程是抢占不过中断的,只有中断之间能抢占)。有了上面绑定 cpu 的经验,我们就把显示中断绑到 cpu3 上去,减少其他中断对显示中断的影响。

文章目录
  1. 1. VR 为何需要性能模式
  2. 2. 场景模式
  3. 3. 设置渲染线程优先级
  4. 4. cpu 绑定
    1. 4.1. 绑定渲染线程 cpu
    2. 4.2. 绑定显示中断 cpu